package com.ipudong.widget.ruler;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.media.AudioAttributes;
import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;

import com.ipudong.widget.R;
import com.ipudong.widget.numberpicker.Scroller;


public class RulerView extends View {

	private static final String tag = "ruler";

	private final Rect canvasRect = new Rect ();
	private RulerCanvas rulerCanvas;
	private static final int SELECTOR_MAX_FLING_VELOCITY_ADJUSTMENT = 6;

	private int mPreviousScrollerX;
	private float mLastDownEventX;
	private float mLastDownOrMoveEventX;
	private VelocityTracker mVelocityTracker;
	private Scroller mFlingScroller;
	private int mScrollState = OnScrollListener.SCROLL_STATE_IDLE;
	private OnScrollListener mOnScrollListener;
	private int mMaximumFlingVelocity;
	private int mMinimumFlingVelocity;
	private int mTouchSlop = 8;

//	private SoundPool soundPool;
//	private int rawSoundId;
//
//	public void setMaxRange (float maxRange) {
//		this.rulerCanvas.maxRange = maxRange;
//	}
//
//	public void setMinRange (float minRange) {
//		this.rulerCanvas.minRange = minRange;
//	}

	public void setDisplayStep (float displayStep) {
		this.rulerCanvas.displayStep = displayStep;
	}

	public void setDisplayPrecision (int displayPrecision) {
		this.rulerCanvas.mDisplayPrecision = displayPrecision;
	}

	public RulerView (Context context) {
		this (context, null);
	}

	public RulerView (Context context, AttributeSet attrs) {
		this (context, attrs, 0);
	}

	public RulerView (Context context, AttributeSet attrs, int defStyleAttr) {
		super (context, attrs, defStyleAttr);

		if (isInEditMode ()) {
			return;
		}
		// 初始化绘图线程
		this.rulerCanvas = new RulerCanvas ();
		// 初始化属性配置
		this.rulerCanvas.initAttr (context, attrs, defStyleAttr);
		// 创建惯性scroller
		this.mFlingScroller = new Scroller (getContext (), null, true);

		ViewConfiguration configuration = ViewConfiguration.get (context);
		mTouchSlop = configuration.getScaledTouchSlop () / 3;
		mMinimumFlingVelocity = configuration.getScaledMinimumFlingVelocity ();
		mMaximumFlingVelocity = configuration.getScaledMaximumFlingVelocity () / SELECTOR_MAX_FLING_VELOCITY_ADJUSTMENT;


		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
			Log.d ("Sound", "Initialize Audio Attributes.");
			// Initialize AudioAttributes.
			AudioAttributes attributes = new AudioAttributes.Builder ()
					.setUsage (AudioAttributes.USAGE_GAME)
					.setContentType (AudioAttributes.CONTENT_TYPE_SONIFICATION)
					.build ();

			Log.d ("Sound", "Set AudioAttributes for SoundPool.");
			// Set the audioAttributes for the SoundPool and specify maximum number of streams.
//			soundPool = new SoundPool.Builder ()
//					.setAudioAttributes (attributes)
//					.setMaxStreams (1)
//					.build ();

		} else {
//			this.soundPool = new SoundPool (1, AudioManager.STREAM_MUSIC, 0);
		}
//		rawSoundId = soundPool.load (getContext (), R.raw.pd_ruler_scroller, 1);
	}

	private void debugPrint () {

		Rect localVisibleRect = new Rect ();
		getLocalVisibleRect (localVisibleRect);
		Log.d (tag, "[getLocalVisibleRect] left = " + localVisibleRect.left);
		Log.d (tag, "[getLocalVisibleRect] right = " + localVisibleRect.right);
		Log.d (tag, "[getLocalVisibleRect] top = " + localVisibleRect.top);
		Log.d (tag, "[getLocalVisibleRect] bottom = " + localVisibleRect.bottom);
		Log.d (tag, "[getLocalVisibleRect] centerX = " + localVisibleRect.centerX ());
		Log.d (tag, "[getLocalVisibleRect] centerY = " + localVisibleRect.centerY ());

		Rect globalVisibleRect = new Rect ();
		getGlobalVisibleRect (globalVisibleRect);
		Log.d (tag, "[getGlobalVisibleRect] left = " + globalVisibleRect.left);
		Log.d (tag, "[getGlobalVisibleRect] right = " + globalVisibleRect.right);
		Log.d (tag, "[getGlobalVisibleRect] top = " + globalVisibleRect.top);
		Log.d (tag, "[getGlobalVisibleRect] bottom = " + globalVisibleRect.bottom);
		Log.d (tag, "[getGlobalVisibleRect] centerX = " + globalVisibleRect.centerX ());
		Log.d (tag, "[getGlobalVisibleRect] centerY = " + globalVisibleRect.centerY ());

		int[] inWindowArr = new int[2];
		getLocationInWindow (inWindowArr);
		Log.d (tag, "[getLocationInWindow] x = " + inWindowArr[0]);
		Log.d (tag, "[getLocationInWindow] y = " + inWindowArr[1]);
		int[] onScreenArr = new int[2];
		getLocationOnScreen (onScreenArr);
		Log.d (tag, "[getLocationOnScreen] x = " + onScreenArr[0]);
		Log.d (tag, "[getLocationOnScreen] y = " + onScreenArr[1]);

		Log.d (tag, "left = " + getLeft ());
		Log.d (tag, "right = " + getRight ());
		Log.d (tag, "top = " + getTop ());
		Log.d (tag, "bottom = " + getBottom ());
	}


	/**
	 * MeasureSpec的specMode,一共三种类型：
	 * <p>
	 * EXACTLY：一般是设置了明确的值或者是MATCH_PARENT
	 * <p>
	 * AT_MOST：表示子布局限制在一个最大值内，一般为WARP_CONTENT
	 * <p>
	 * UNSPECIFIED：表示子布局想要多大就多大，很少使用
	 *
	 * @param widthMeasureSpec  widthMeasureSpec
	 * @param heightMeasureSpec heightMeasureSpec
	 */
	@Override
	protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec) {
		super.onMeasure (widthMeasureSpec, heightMeasureSpec);

		// Try greedily to fit the max width and height.
		/*
	  The max width of this widget.
     */
		int mMaxWidth = SIZE_UNSPECIFIED;
		final int newWidthMeasureSpec = makeMeasureSpec (widthMeasureSpec, mMaxWidth);
		/*
	  The max height of this widget.
     */
		int mMaxHeight = SIZE_UNSPECIFIED;
		final int newHeightMeasureSpec = makeMeasureSpec (heightMeasureSpec, mMaxHeight);
		super.onMeasure (newWidthMeasureSpec, newHeightMeasureSpec);
		// Flag if we are measured with width or height less than the respective min.
		/*
	  The max width of this widget.
     */
		int mMinWidth = SIZE_UNSPECIFIED;
		final int widthSize = resolveSizeAndStateRespectingMinSize (mMinWidth, getMeasuredWidth (),
				widthMeasureSpec);
		/*
	  The min height of this widget.
     */
		int mMinHeight = SIZE_UNSPECIFIED;
		final int heightSize = resolveSizeAndStateRespectingMinSize (mMinHeight, getMeasuredHeight (),
				heightMeasureSpec);
		setMeasuredDimension (widthSize, heightSize);

		debugPrint ();

	}


	@Override
	protected void onDraw (Canvas canvas) {
		super.onDraw (canvas);


		getLocalVisibleRect (canvasRect);
		if (this.rulerCanvas != null) {
			this.rulerCanvas.loadWindowParam (canvasRect);
			this.rulerCanvas.draw (canvas);
		} else {
			Log.w (tag, "[onDraw] rulerCanvas == null");
		}
	}

	private static final int SIZE_UNSPECIFIED = -1;

	/**
	 * Makes a measure spec that tries greedily to use the max value.
	 *
	 * @param measureSpec The measure spec.
	 * @param maxSize     The max value for the size.
	 * @return A measure spec greedily imposing the max size.
	 */
	private int makeMeasureSpec (int measureSpec, int maxSize) {
		if (maxSize == SIZE_UNSPECIFIED) {
			return measureSpec;
		}
		final int size = MeasureSpec.getSize (measureSpec);
		final int mode = MeasureSpec.getMode (measureSpec);
		switch (mode) {
			case MeasureSpec.EXACTLY:
				return measureSpec;
			case MeasureSpec.AT_MOST:
				return MeasureSpec.makeMeasureSpec (Math.min (size, maxSize), MeasureSpec.EXACTLY);
			case MeasureSpec.UNSPECIFIED:
				return MeasureSpec.makeMeasureSpec (maxSize, MeasureSpec.EXACTLY);
			default:
				throw new IllegalArgumentException ("Unknown measure mode: " + mode);
		}
	}

	/**
	 * Utility to reconcile a desired size and state, with constraints imposed
	 * by a MeasureSpec. Tries to respect the min size, unless a different size
	 * is imposed by the constraints.
	 *
	 * @param minSize      The minimal desired size.
	 * @param measuredSize The currently measured size.
	 * @param measureSpec  The current measure spec.
	 * @return The resolved size and state.
	 */
	private int resolveSizeAndStateRespectingMinSize (
			int minSize, int measuredSize, int measureSpec) {
		if (minSize != SIZE_UNSPECIFIED) {
			final int desiredWidth = Math.max (minSize, measuredSize);
			if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
				return resolveSizeAndState (desiredWidth, measureSpec, 0);
			} else {
				return getDefaultSize (measuredSize, measureSpec);
			}
		} else {
			return getDefaultSize (measuredSize, measureSpec);
		}
	}

	@Override
	public boolean onTouchEvent (MotionEvent event) {

		if (mVelocityTracker == null) {
			mVelocityTracker = VelocityTracker.obtain ();
		}
		mVelocityTracker.addMovement (event);
		final int action = event.getAction ();
		switch (action) {
			case MotionEvent.ACTION_DOWN:

				mLastDownEventX = mLastDownOrMoveEventX = event.getX ();
				if (!mFlingScroller.isFinished ()) {
					mFlingScroller.abortAnimation ();
					onScrollStateChange (OnScrollListener.SCROLL_STATE_IDLE);
					Log.d (tag, "[ACTION_DOWN] stop fling.");
				} else {
					Log.d (tag, "[ACTION_DOWN] ...");
				}
				mVelocityTracker.clear ();
				mVelocityTracker.addMovement (event);
				break;
			case MotionEvent.ACTION_MOVE:

				final float currentMoveX = event.getX ();
				if (mScrollState != OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {

					final int deltaDownX = (int) Math.abs (currentMoveX - mLastDownEventX);
					if (deltaDownX >= mTouchSlop) {
						onScrollStateChange (OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
						Log.d (tag, "[ACTION_DOWN ->  ACTION_MOVE$onScrollStateChange] mScrollState = SCROLL_STATE_TOUCH_SCROLL " + "... deltaDownX = " + deltaDownX + " , mTouchSlop = " + mTouchSlop);
					} else {
						Log.d (tag, "[ACTION_DOWN ->  ACTION_MOVE] mScrollState = " + (mScrollState == 0 ? "SCROLL_STATE_IDLE" : "SCROLL_STATE_FLING") + "...  deltaDownX = " + deltaDownX + " , mTouchSlop = " + mTouchSlop);
					}

				} else {
					scrollBy ((int) (currentMoveX - mLastDownOrMoveEventX), 0);
				}
				mLastDownOrMoveEventX = currentMoveX;
				break;
			case MotionEvent.ACTION_UP:

				VelocityTracker velocityTracker = mVelocityTracker;
				velocityTracker.computeCurrentVelocity (4000, mMaximumFlingVelocity);
				int initialVelocity = (int) velocityTracker.getXVelocity ();

				float tap = mLastDownEventX - event.getX ();
				Log.d (tag, "[ACTION_UP] tap = " + tap + "initialVelocity = " + initialVelocity);
				if (tap > 0 && initialVelocity < 0 || tap < 0 && initialVelocity > 0) {
					// 手指离开后,根据加速度进行滑动
					if (Math.abs (initialVelocity) > mMinimumFlingVelocity) {
						Log.d (tag, "[ACTION_UP] fling(" + initialVelocity + ") mMinimumFlingVelocity = " + mMinimumFlingVelocity);
						fling (initialVelocity);
						onScrollStateChange (OnScrollListener.SCROLL_STATE_FLING);

					} else {
						scrollBy ((int) (event.getX () - mLastDownOrMoveEventX), 0);
						onScrollStateChange (OnScrollListener.SCROLL_STATE_IDLE);
					}
				} else {
					Log.w (tag, "fix 反弹...不处理");
					scrollBy ((int) (event.getX () - mLastDownOrMoveEventX), 0);
					onScrollStateChange (OnScrollListener.SCROLL_STATE_IDLE);
				}


				mVelocityTracker.recycle ();
				mVelocityTracker = null;
				break;
			case MotionEvent.ACTION_CANCEL:
				if (!mFlingScroller.isFinished ()) {
					mFlingScroller.abortAnimation ();
					onScrollStateChange (OnScrollListener.SCROLL_STATE_IDLE);
					Log.d (tag, "[ACTION_CANCEL] onScrollStateChange = SCROLL_STATE_IDLE");
				} else {
					Log.d (tag, "[ACTION_CANCEL] ...");
				}
				break;
		}

		return true;
	}


	private int mTouchSlopOverlay = 0;

	@Override
	public void scrollBy (int x, int y) {

		if (rulerCanvas == null) {
			Log.w (tag, "rulerCanvas == null");
			return;
		}
		final int tickWidth = rulerCanvas.getTickUnit ();
		mTouchSlopOverlay += x;
		if (Math.abs (mTouchSlopOverlay) >= tickWidth) {
			int slop = mTouchSlopOverlay % tickWidth;
			// 累计以往的距离,计算本次可以滑动的距离.
			x = mTouchSlopOverlay - slop;
			mTouchSlopOverlay = slop;
			super.scrollBy (x, y);
			Log.d (tag, String.format ("[scrollBy] x = %d, y = %d", x, y));
			rulerCanvas.validateTap (x, "scrollBy");
		} else {


			if (mScrollState == OnScrollListener.SCROLL_STATE_FLING) {
				Log.d (tag, "[scrollBy -> SCROLL_STATE_FLING] ignore ...x = " + x + ", y = " + x);
				Log.w (tag, "[scrollBy -> SCROLL_STATE_FLING] invalidate() 确保滑动事件没有被异常终止..");
				invalidate ();
			} else {
				Log.d (tag, "[scrollBy -> SCROLL_STATE_TOUCH_SCROLL] ignore ...x = " + x + ", y = " + x);
				Log.w (tag, "[scrollBy -> SCROLL_STATE_TOUCH_SCROLL] ... ");
			}

		}
	}

	@Override
	public void computeScroll () {
		super.computeScroll ();
		Scroller scroller = mFlingScroller;
		if (scroller == null) {
			Log.w (tag, "[computeScroll] scroller == null");
			return;
		}
		if (scroller.isFinished ()) {
			Log.d (tag, "[computeScroll] return [null].");
			return;
		}
		// scroll animation is not stop.
		if (scroller.computeScrollOffset ()) {
			int currentScrollerX = scroller.getCurrX ();
			if (mPreviousScrollerX == 0) {
				mPreviousScrollerX = scroller.getStartX ();
			}
			scrollBy (currentScrollerX - mPreviousScrollerX, 0);
			mPreviousScrollerX = currentScrollerX;
		}
		if (scroller.isFinished ()) {
			onScrollerFinished (scroller);
		}
	}

	/**
	 * Callback invoked upon completion of a given <code>scroller</code>.
	 */
	private void onScrollerFinished (Scroller scroller) {
		if (scroller == mFlingScroller) {
			onScrollStateChange (OnScrollListener.SCROLL_STATE_IDLE);
		}
//        rulerCanvas.setLoopDraw(false);
	}


	/**
	 * Set mSelectedListener to be notified for scroll state changes.
	 *
	 * @param onScrollListener The mSelectedListener.
	 */
	public void setOnScrollListener (OnScrollListener onScrollListener) {
		mOnScrollListener = onScrollListener;
	}

	/**
	 * Handles transition to a given <code>scrollState</code>
	 */
	private void onScrollStateChange (int scrollState) {
		if (mScrollState == scrollState) {
			return;
		}
		Log.d (tag, "[onScrollStateChange] scrollState = " + scrollState);
		mScrollState = scrollState;
		if (mScrollState == OnScrollListener.SCROLL_STATE_IDLE) {
			mTouchSlopOverlay = 0;
//            rulerCanvas.setLoopDraw(false);
		}
		if (mOnScrollListener != null) {
			mOnScrollListener.onScrollStateChange (this, scrollState);
		}
		if (mSelectedListener != null) {
			Log.d ("chenjunqi", "onScrollStateChange: " + this.rulerCanvas.getPosition () + " = " + this.rulerCanvas.getCenterValue ());
			mSelectedListener.onValueSelected (this.rulerCanvas.getCenterValue ());
		}
	}

	/**
	 * Flings the selector with the given <code>velocityY</code>.
	 */
	private void fling (int velocityX) {

		mPreviousScrollerX = 0;

		int velocityY = 0;
		int minX = Integer.MIN_VALUE;
		int maxX = Integer.MAX_VALUE;
		int minY = 0;
		int maxY = 0;

		Log.d (tag, "[fling] startX = " + getScrollX () + ", velocityX = " + velocityX);
		mFlingScroller.fling (getScrollX (), getScrollY (), velocityX, velocityY, minX, maxX, minY, maxY);

		invalidate ();
	}

	/**
	 * Interface to listen for the picker scroll state.
	 */
	public interface OnScrollListener {

		/**
		 * The view is not scrolling.
		 */
		int SCROLL_STATE_IDLE = 0;

		/**
		 * The user is scrolling using touch, and his finger is still on the screen.
		 */
		int SCROLL_STATE_TOUCH_SCROLL = 1;

		/**
		 * The user had previously been scrolling using touch and performed a fling.
		 */
		int SCROLL_STATE_FLING = 2;

		/**
		 * Callback invoked while the number picker scroll state has changed.
		 *
		 * @param view        The view whose scroll state is being reported.
		 * @param scrollState The current scroll state. One of
		 *                    {@link #SCROLL_STATE_IDLE},
		 *                    {@link #SCROLL_STATE_TOUCH_SCROLL} or
		 *                    {@link #SCROLL_STATE_IDLE}.
		 */
		void onScrollStateChange (RulerView view, int scrollState);
	}

	public interface OnSelectedListener {
		void onValueSelected (String value);

		void onValueChanged (String value);
	}

	private OnSelectedListener mSelectedListener;

	public void setSelectedListener (OnSelectedListener mSelectedListener) {
		this.mSelectedListener = mSelectedListener;
	}

	@Deprecated
	public void setSelectValue (float value) {
		this.rulerCanvas.setDisplayValue (value);
	}

	public class RulerCanvas {

		public static final int DISPLAY_PRECISION_INTEGER = 0;
		public static final int DISPLAY_PRECISION_FLOAT = 1;

		public static final String tag = "RulerCanvasThread";

		/**
		 * 刻度线的单位高度,即真实高度 = 单位高度(px) * 单位数
		 */
		int unit;
		/**
		 * 每格刻度线的间距,默认刻度线是均分的.(px)
		 */
		int tickMarkInterval;


		/**
		 * 配置高度,运行时会被转化成px像素值
		 */
		final int[] tickHeightConfiguration = new int[]{44, 18, 18, 18, 18, 24, 18, 18, 18, 18};

		int selectedCenterHeight = 52;
		int selectedCenterWidth = 4;

		int displayValueTextSizeBelowTick;
		int displayValueColorBelowTick;
		int tickColor;
		// 刻度画笔
		private Paint tickPaint;
		// 中间标线画笔
		private Paint midPaint;
		// 整点刻度的文案画笔
		private Paint tickDescriptionPaint;
		private Paint borderPaint;
		//        private volatile boolean isLoopDraw = false;
//        private boolean isFirstDraw = true;
//		private float maxRange = Integer.MAX_VALUE;
//		private float minRange = Integer.MIN_VALUE;
		private float displayStep;
		private int mDisplayPrecision;

		// 记录指针滑动过的位移
		volatile int position = 0;

		private boolean isBorder (int position) {
			return (Math.abs (position) % tickHeightConfiguration.length == 0);
		}

		public int getPosition () {
			return position;
		}

		int selectedCenterColor;

		private void initPaint () {
			{
				this.tickPaint = new Paint ();
				this.tickPaint.setColor (tickColor);
				this.tickPaint.setAntiAlias (true);
			}
			{
				this.midPaint = new Paint ();
				this.midPaint.setColor (selectedCenterColor);
				this.midPaint.setAntiAlias (true);

			}
			{
				this.tickDescriptionPaint = new Paint ();
				this.tickDescriptionPaint.setTextSize (displayValueTextSizeBelowTick);
				this.tickDescriptionPaint.setAntiAlias (true);
				this.tickDescriptionPaint.setColor (displayValueColorBelowTick);
			}
			{
				this.borderPaint = new Paint ();
				this.borderPaint.setColor (tickColor);
				this.borderPaint.setStrokeWidth (getTickWidth ());
				this.borderPaint.setStyle (Paint.Style.STROKE);
			}
		}


		public RulerCanvas () {
		}

		private int[] mappingConguration (int[] configuration, int unitPx) {
			if (configuration == null) return null;
			final int length = configuration.length;
			final int[] configurationMap = new int[length];
			for (int i = 0; i < length; i++) {
				configurationMap[i] = configuration[i] * unitPx;
			}
			return configurationMap;
		}


		private Rect canvasRect = null;
		private final Rect midRect = new Rect ();
		private float centerValue = 0;
		private int drawLeftStart;
		private int drawRightStart;

		public void loadWindowParam (Rect rect) {

			this.canvasRect = rect;

			this.midRect.left = this.canvasRect.centerX () - selectedCenterWidth / 2;
			this.midRect.right = this.midRect.left + selectedCenterWidth;
			this.midRect.top = 0;
			this.midRect.bottom = midRect.top + selectedCenterHeight * unit;

			// 刻度线的宽度中心,因为每条刻度线可能不一样宽
			// 这里之所以要错开一个位置,是为了使从中间开始绘制的时不出现重复
			this.drawLeftStart = midRect.right + tickMarkInterval;
			this.drawRightStart = midRect.left - tickMarkInterval;
			Log.d (tag, "loadWindowParam :[" + midRect.left + "," + midRect.right + "," + midRect.top + "," + midRect.bottom + "]");
			Log.d (tag, "loadWindowParam :[" + drawLeftStart + " ," + drawRightStart + "]");
		}


//		int maxRangePosition;
//		int minRangePosition;

		public void setDisplayValue (float centerValue) {


			// 同时放大十倍是为了保证精度不丢失
			final int step = (int) (displayStep * 10);
			final int center = (int) (centerValue * 10);

			if (center == 0 || step == 0) {
				return;
			} else {
				int slop = center % (step * 10);
				Log.d (tag, "slop = " + slop);
				if (slop != 0) {
					// 使用10的倍数作为边界,若哟溢出则执行滑动..
					this.position += slop / step;
					this.centerValue = centerValue - this.position * displayStep;
				} else {
					this.centerValue = centerValue;
					this.position = 0;
				}
//				this.minRangePosition = -((int) ((centerValue * 10 - minRange * 10) / step) - this.position);
//				this.maxRangePosition = (int) ((maxRange * 10 - centerValue * 10) / step) + this.position;
//				Log.d (tag, "[validateTap] min:" + minRangePosition + " , cur:" + position + " , max:" + maxRangePosition);
			}
			invalidate ();

			if (mSelectedListener != null) {
				Log.d ("chenjunqi", "setDisplayValue: " + this.position + " = " + getDisplayValueStr (this.position));
				mSelectedListener.onValueChanged (getDisplayValueStr (this.position));
			}

		}

		public String getDisplayValueStr (int position) {
			float ret = centerValue + position * displayStep;
			if (DISPLAY_PRECISION_INTEGER == mDisplayPrecision) {
				return String.valueOf ((int) ret);
			} else {
				String format = "%." + mDisplayPrecision + "f";
				return String.format (format, ret);
			}
		}

		public String getCenterValue () {
			return getDisplayValueStr (position);
		}


		/**
		 * Move方法中根据X的差值,来移动..
		 *
		 * @param debug debug
		 * @param tap   tap
		 */
		public void validateTap (int tap, String debug) {

			// 使方向感...弄正..坑爹..方向感差
			tap = -tap;
			int tapPosition = (tap / getTickUnit ());
			int position = tapPosition + this.position;
			if (position <= Integer.MIN_VALUE) {
				this.position = Integer.MIN_VALUE;
//				soundPool.stop (rawSoundId);
			} else if (position >= Integer.MAX_VALUE) {
				this.position = Integer.MAX_VALUE;
//				soundPool.stop (rawSoundId);
			} else {
				Log.d (tag, debug + "[validateTap] tapPosition = " + tapPosition + " ,  定位到 position = " + position);
				this.position = position;
//				soundPool.play (rawSoundId, 0.5f, 0.5f, 0, 0, 1);
			}
			if (mSelectedListener != null && tapPosition != 0) {
				Log.d ("chenjunqi", "validateTap: " + this.position + " = " + getDisplayValueStr (this.position));
				mSelectedListener.onValueChanged (getDisplayValueStr (this.position));
			}
			invalidate ();
		}

		public void initAttr (Context context, AttributeSet attrs, int defStyleAttr) {
			TypedArray a = null;
			try {
				a = context.getTheme ().obtainStyledAttributes (attrs, R.styleable.HorizontalScrollRulerView, defStyleAttr, 0);
				int n = a.getIndexCount ();
				for (int i = 0; i < n; i++) {
					final int attr = a.getIndex (i);
					if (attr == R.styleable.HorizontalScrollRulerView_tickMarkInterval) {
						this.tickMarkInterval = a.getDimensionPixelSize (attr, (int) TypedValue.applyDimension (
								TypedValue.COMPLEX_UNIT_DIP, 0.5f, context.getResources ().getDisplayMetrics ()));
					} else if (attr == R.styleable.HorizontalScrollRulerView_tickMarkUnit) {
						this.unit = a.getDimensionPixelSize (attr, (int) TypedValue.applyDimension (
								TypedValue.COMPLEX_UNIT_DIP, 0.5f, context.getResources ().getDisplayMetrics ()));
					} else if (attr == R.styleable.HorizontalScrollRulerView_displayCenterValue) {
						int displayCenterValue = a.getInt (attr, 10);
						setDisplayValue (displayCenterValue);
					} else if (attr == R.styleable.HorizontalScrollRulerView_displayStep) {
						this.displayStep = a.getFloat (attr, 0.5f);
					} else if (attr == R.styleable.HorizontalScrollRulerView_selectedCenterWidth) {
						this.selectedCenterWidth = a.getDimensionPixelSize (attr, (int) TypedValue.applyDimension (
								TypedValue.COMPLEX_UNIT_DIP, 4f, context.getResources ().getDisplayMetrics ()));
					} else if (attr == R.styleable.HorizontalScrollRulerView_selectedCenterHeight) {
						this.selectedCenterHeight = a.getDimensionPixelSize (attr, (int) TypedValue.applyDimension (
								TypedValue.COMPLEX_UNIT_DIP, 52f, context.getResources ().getDisplayMetrics ()));
					} else if (attr == R.styleable.HorizontalScrollRulerView_displayValueColorBelowTick) {
						this.displayValueColorBelowTick = a.getColor (attr, Color.BLACK);
					} else if (attr == R.styleable.HorizontalScrollRulerView_displayValueTextSizeBelowTick) {
						this.displayValueTextSizeBelowTick = a.getDimensionPixelSize (attr, (int) TypedValue.applyDimension (
								TypedValue.COMPLEX_UNIT_DIP, 22f, context.getResources ().getDisplayMetrics ()));
					} else if (attr == R.styleable.HorizontalScrollRulerView_tickColor) {
						this.tickColor = a.getColor (attr, Color.parseColor ("#E94E4C"));
					} else if (attr == R.styleable.HorizontalScrollRulerView_selectedCenterColor) {
						this.selectedCenterColor = a.getColor (attr, Color.parseColor ("#348FC4"));
					} else if (attr == R.styleable.HorizontalScrollRulerView_displayPrecision) {
						this.mDisplayPrecision = a.getInt (attr, DISPLAY_PRECISION_INTEGER);
					}
				}
			} finally {
				if (a != null) {
					a.recycle ();
				}
			}

			//初始化画笔
			initPaint ();
			// 初始化单元模板高度
			mappingConguration (tickHeightConfiguration, unit);

			StringBuilder debugHeightConfiguration = new StringBuilder ();
			for (int aTickHeightConfiguration : tickHeightConfiguration) {
				debugHeightConfiguration.append (aTickHeightConfiguration);
				debugHeightConfiguration.append (",");
			}
			Log.d (tag, "tickHeightConfiguration = " + debugHeightConfiguration.toString ());

		}

		public int getTickMarkInterval () {
			return tickMarkInterval;
		}

		int tickUnit = -1;

		public int getTickUnit () {

			if (tickUnit == -1) {
				int interval = getTickMarkInterval ();//格子间距
				int tickWidth = getTickWidth ();
				tickUnit = interval + tickWidth;
			}

			return tickUnit;
		}

		int tickWidth = -1;

		private int getTickWidth () {
			if (tickWidth == -1) {
				tickWidth = 2 * unit;
			}
			return tickWidth;
		}

		private int getTickHeight (int position) {
			return tickHeightConfiguration[Math.abs (position) % tickHeightConfiguration.length];
		}


		public void draw (Canvas canvas) {
			Log.d (tag, " draw canvas");

			canvas.drawRect (canvasRect.left + getTickWidth () / 2, canvasRect.top + getTickWidth () / 2, canvasRect.right - getTickWidth () / 2, canvasRect.bottom - getTickWidth () / 2, borderPaint);

			Log.d (tag, "drawLeftStart = " + drawLeftStart + ",drawRightStart = " + drawRightStart);
			// 向左copy刻度尺
			drawLeftTickFromMiddle (canvas, position, drawLeftStart);
			// 向右copy刻度尺
			drawRightTickFromMiddle (canvas, position, drawRightStart);
			// 画中间的刻度标尺
			canvas.drawRect (midRect, midPaint);
		}


		private void drawLeftTickFromMiddle (Canvas canvas, int position, int leftX) {

			if (leftX < canvasRect.left) {
				return;
			}

			final int tickHeight = getTickHeight (position);
			final int tickWidth = getTickWidth ();
			final int left = leftX - tickMarkInterval - tickWidth;
			final int right = leftX - tickMarkInterval;
			final int top = 0;
			final int bottom = top + tickHeight;

			canvas.drawRect (left, top, right, bottom, tickPaint);

			if (isBorder (position)) {
				int padding = 50;
				int x = (left + right) / 2;
				int y = (bottom + padding);
				String text = getDisplayValueStr (position);
				int textWidth = (int) tickDescriptionPaint.measureText (text);
				canvas.drawText (text, x - textWidth / 2, y, tickDescriptionPaint);
			}

			int prevPosition = position - 1;
			drawLeftTickFromMiddle (canvas, prevPosition, left);
		}

		private void drawRightTickFromMiddle (Canvas canvas, int position, int rightX) {

			if (rightX > canvasRect.right) {
				return;
			}

			final int tickHeight = getTickHeight (position);
			final int tickWidth = getTickWidth ();

			int left = rightX + tickMarkInterval;
			int right = rightX + tickMarkInterval + tickWidth;
			int top = 0;
			int bottom = top + tickHeight;

			canvas.drawRect (left, top, right, bottom, tickPaint);
			if (isBorder (position)) {
				int padding = 50;
				float x = (left + right) / 2;
				int y = (bottom + padding);
				String text = getDisplayValueStr (position);
				int textWidth = (int) tickDescriptionPaint.measureText (text);
				canvas.drawText (text, x - textWidth / 2, y, tickDescriptionPaint);
			}

			int nextPosition = position + 1;
			drawRightTickFromMiddle (canvas, nextPosition, right);
		}
	}

	@Override
	protected void onDetachedFromWindow () {
		super.onDetachedFromWindow ();
//		soundPool.unload (rawSoundId);
//		soundPool.release ();
	}
}
